package com.caishi.lkx.utils;

import com.caishi.lkx.distribution.properties.WeixinModel;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.annotations.SerializedName;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.List;
import java.util.Map;



/**
 * 微信转账bean
 */
@Component
public class TransferToUser {
    private static String HOST = "https://api.mch.weixin.qq.com";
    private static String METHOD = "POST";
    private static String PATH = "/v3/fund-app/mch-transfer/transfer-bills";

    private  String mchid;
    private  String certificateSerialNo;
    private PrivateKey privateKey;
    private  String wechatPayPublicKeyId;
    private PublicKey wechatPayPublicKey;
    public PublicKey platformKey;

    private static final String WECHAT_OAUTH2_ACCESS_TOKEN_URL =
            "https://api.weixin.qq.com/sns/oauth2/access_token?" +
                    "appid=%s&secret=%s&code=%s&grant_type=authorization_code";
    private static final String WECHAT_USER_INFO_URL =
            "https://api.weixin.qq.com/sns/userinfo?" +
                    "access_token=%s&openid=%s";
    @Autowired
    public TransferToUser(WeixinModel weixinModel) throws Exception {
        this.mchid = weixinModel.getMchid();
        this.certificateSerialNo = weixinModel.getApiSerialNumber();

        InputStream resourceAsStream = TransferToUser.class.getClassLoader().getResourceAsStream(weixinModel.getApiKey());
        this.privateKey = WXPayUtility.loadPrivateKeyFromString(new String(resourceAsStream.readAllBytes(), StandardCharsets.UTF_8));

        resourceAsStream.close();
        this.wechatPayPublicKeyId=weixinModel.getPaySerialNumber();


        InputStream resourceAsStream1 = TransferToUser.class.getClassLoader().getResourceAsStream(weixinModel.getPayPublicKey());
        this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromString(new String(resourceAsStream1.readAllBytes(), StandardCharsets.UTF_8));
        resourceAsStream1.close();

        this.platformKey=WechatPaySignatureVerifier.loadWechatPayPublicKey(weixinModel.getPlatformKey());
    }

    public PublicKey getPlatformKey() {
        return platformKey;
    }

    public static Map<String,Object> getUserInfo(String accessToken, String openid) throws JsonProcessingException {
        RestTemplate restTemplate = new RestTemplate();

        String url = String.format(WECHAT_USER_INFO_URL, accessToken, openid);

        ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
        String body = responseEntity.getBody();

        ObjectMapper mapper = new ObjectMapper();
        return mapper.readValue(body, Map.class);
    }
    public static Map<String, Object> getOpenId(String appId, String appSecret, String code) throws Exception {
        RestTemplate restTemplate = new RestTemplate();

        String url = String.format(WECHAT_OAUTH2_ACCESS_TOKEN_URL, appId, appSecret, code);

        ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
        String body = responseEntity.getBody();

        ObjectMapper mapper = new ObjectMapper();
        return mapper.readValue(body, Map.class);
    }
    public TransferToUserResponse run(TransferToUserRequest request) {
        String uri = PATH;
        String reqBody = WXPayUtility.toJson(request);

        Request.Builder reqBuilder = new Request.Builder().url(HOST + uri);
        reqBuilder.addHeader("Accept", "application/json");
        reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId);
        reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo,privateKey, METHOD, uri, reqBody));
        reqBuilder.addHeader("Content-Type", "application/json");
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody);
        reqBuilder.method(METHOD, requestBody);
        Request httpRequest = reqBuilder.build();

        // 发送HTTP请求
        OkHttpClient client = new OkHttpClient.Builder().build();
        try (Response httpResponse = client.newCall(httpRequest).execute()) {
            String respBody = WXPayUtility.extractBody(httpResponse);
            if (httpResponse.code() >= 200 && httpResponse.code() < 300) {
                // 2XX 成功，验证应答签名
                WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey,
                        httpResponse.headers(), respBody);

                // 从HTTP应答报文构建返回数据
                return WXPayUtility.fromJson(respBody, TransferToUserResponse.class);
            } else {
                throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers());
            }
        } catch (IOException e) {
            throw new UncheckedIOException("Sending request to " + uri + " failed.", e);
        }
    }

    public String getMchid() {
        return mchid;
    }

    public void setMchid(String mchid) {
        this.mchid = mchid;
    }

    public String getCertificateSerialNo() {
        return certificateSerialNo;
    }

    public void setCertificateSerialNo(String certificateSerialNo) {
        this.certificateSerialNo = certificateSerialNo;
    }

    public PrivateKey getPrivateKey() {

        return privateKey;
    }

    public void setPrivateKey(String privateKeyFilePath) throws IOException {
        InputStream resourceAsStream = TransferToUser.class.getClassLoader().getResourceAsStream(privateKeyFilePath);
        this.privateKey = WXPayUtility.loadPrivateKeyFromString(new String(resourceAsStream.readAllBytes(), StandardCharsets.UTF_8));

        resourceAsStream.close();

    }

    public String getWechatPayPublicKeyId() {
        return wechatPayPublicKeyId;
    }

    public void setWechatPayPublicKeyId(String wechatPayPublicKeyId) {
        this.wechatPayPublicKeyId = wechatPayPublicKeyId;
    }

    public PublicKey getWechatPayPublicKey() {
        return wechatPayPublicKey;
    }

    public void setWechatPayPublicKey(String wechatPayPublicKeyPath) throws IOException {
        InputStream resourceAsStream = TransferToUser.class.getClassLoader().getResourceAsStream(wechatPayPublicKeyPath);
        this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromString(new String(resourceAsStream.readAllBytes(), StandardCharsets.UTF_8));
        resourceAsStream.close();
    }

    public String encrypt(String plainText) {
        return WXPayUtility.encrypt(this.wechatPayPublicKey, plainText);
    }

    public static class TransferToUserResponse {
        @SerializedName("out_bill_no")
        public String outBillNo;

        @SerializedName("transfer_bill_no")
        public String transferBillNo;

        @SerializedName("create_time")
        public String createTime;

        @SerializedName("state")
        public TransferBillStatus state;

        @SerializedName("package_info")
        public String packageInfo;
    }

    public enum TransferBillStatus {
        @SerializedName("ACCEPTED")
        ACCEPTED,
        @SerializedName("PROCESSING")
        PROCESSING,
        @SerializedName("WAIT_USER_CONFIRM")
        WAIT_USER_CONFIRM,
        @SerializedName("TRANSFERING")
        TRANSFERING,
        @SerializedName("SUCCESS")
        SUCCESS,
        @SerializedName("FAIL")
        FAIL,
        @SerializedName("CANCELING")
        CANCELING,
        @SerializedName("CANCELLED")
        CANCELLED
    }

    public static class TransferSceneReportInfo {
        @SerializedName("info_type")
        public String infoType;

        @SerializedName("info_content")
        public String infoContent;
    }

    public static class TransferToUserRequest {
        @SerializedName("appid")
        public String appid;

        @SerializedName("out_bill_no")
        public String outBillNo;

        @SerializedName("transfer_scene_id")
        public String transferSceneId;

        @SerializedName("openid")
        public String openid;

        @SerializedName("user_name")
        public String userName;

        @SerializedName("transfer_amount")
        public Long transferAmount;

        @SerializedName("transfer_remark")
        public String transferRemark;

        @SerializedName("notify_url")
        public String notifyUrl;

        @SerializedName("user_recv_perception")
        public String userRecvPerception;

        @SerializedName("transfer_scene_report_infos")
        public List<TransferSceneReportInfo> transferSceneReportInfos;
    }

}

